////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2017-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  camxmultistatsoperator.cpp
/// @brief Implements the multi statistics operator for multi camera framework.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "camxmultistatsoperator.h"
#include "camxvendortags.h"
#include "chivendortag.h"

CAMX_NAMESPACE_BEGIN

// Dependency table for Master/Follower mode
MultiStatsDependency qtiDependencyTable[] =
{
    {
        StatsAlgoSyncType::StatsAlgoSyncTypeAECAWB, // Represent dependencis from Stats node (Only AEC/AWB)
        StatsAlgoRole::StatsAlgoRoleMaster,         // Role is Master
        {
            {PropertyIDAECPeerInfo, TRUE},          // Cross Pipeline Dependency 0
            {PropertyIDAWBPeerInfo, TRUE},          // Cross Pipeline Dependency 1
            {},                                     // Cross Pipeline Dependency 2
            {},                                     // Cross Pipeline Dependency 3
            {}                                      // Cross Pipeline Dependency 4
        },
        StatsAlgoProcessRequest                     // Action is to perform process request as Master
    },
    {
        StatsAlgoSyncType::StatsAlgoSyncTypeAECAWB, // Represent dependencis from Stats node (Only AEC/AWB)
        StatsAlgoRole::StatsAlgoRoleSlave,          // Role is Slave
        {
            {PropertyIDAECPeerInfo, TRUE},          // Cross Pipeline Dependency 0
            {PropertyIDAWBPeerInfo, TRUE},          // Cross Pipeline Dependency 1
            {},                                     // Cross Pipeline Dependency 2
            {},                                     // Cross Pipeline Dependency 3
            {}                                      // Cross Pipeline Dependency 4
        },
        StatsAlgoProcessMapping                     // Action is to perform mapping procedure as Follower(Slave)
    },
    {
        StatsAlgoSyncType::StatsAlgoSyncTypeAF,     // Represent dependencis from AF node
        StatsAlgoRole::StatsAlgoRoleMaster,         // Role is Master
        {
            {PropertyIDAFPeerInfo, TRUE},           // Cross Pipeline Dependency 0
            {},                                     // Cross Pipeline Dependency 1
            {},                                     // Cross Pipeline Dependency 2
            {},                                     // Cross Pipeline Dependency 3
            {}                                      // Cross Pipeline Dependency 4
        },
        StatsAlgoProcessRequest                     // Action is to perform process request as Master
    },
    {
        StatsAlgoSyncType::StatsAlgoSyncTypeAF,     // Represent dependencis from AF node
        StatsAlgoRole::StatsAlgoRoleSlave,          // Role is Slave
        {
            {PropertyIDAFPeerInfo, TRUE},           // Cross Pipeline Dependency 0
            {},                                     // Cross Pipeline Dependency 1
            {},                                     // Cross Pipeline Dependency 2
            {},                                     // Cross Pipeline Dependency 3
            {},                                     // Cross Pipeline Dependency 4
        },
        StatsAlgoProcessMapping                     // Action is to perform mapping procedure as Follower(Slave)
    }
};

// Dependency table for Role-Switch process
MultiStatsDependency qtiRoleSwitchDependencyTable[] =
{
    {
        StatsAlgoSyncType::StatsAlgoSyncTypeAECAWB, // Represent dependencis from Stats node (Only AEC/AWB)
        StatsAlgoRole::StatsAlgoRoleMaster,         // New role is Master, its previous role was Slave
        {
            {},                                     // Cross Pipeline Dependency 0
            {},                                     // Cross Pipeline Dependency 1
            {},                                     // Cross Pipeline Dependency 2
            {},                                     // Cross Pipeline Dependency 3
            {}                                      // Cross Pipeline Dependency 4
        },
        StatsAlgoProcessSwitchingMaster             // Action is to perform procedure of switching to Master
    },
    {
        StatsAlgoSyncType::StatsAlgoSyncTypeAECAWB, // Represent dependencis from Stats node (Only AEC/AWB)
        StatsAlgoRole::StatsAlgoRoleSlave,          // New role is Slave, its previous role was Master
        {
            {},                                     // Cross Pipeline Dependency 0
            {},                                     // Cross Pipeline Dependency 1
            {},                                     // Cross Pipeline Dependency 2
            {},                                     // Cross Pipeline Dependency 3
            {}                                      // Cross Pipeline Dependency 4
        },
        StatsAlgoProcessSwitchingSlave              // Action is to perform procedure of switching to Slave
    },
    {
        StatsAlgoSyncType::StatsAlgoSyncTypeAF,     // Represent dependencis from AF node
        StatsAlgoRole::StatsAlgoRoleMaster,         // New role is Master, its previous role was Slave
        {
            {},                                     // Cross Pipeline Dependency 0
            {},                                     // Cross Pipeline Dependency 1
            {},                                     // Cross Pipeline Dependency 2
            {},                                     // Cross Pipeline Dependency 3
            {}                                      // Cross Pipeline Dependency 4
        },
        StatsAlgoProcessSwitchingMaster             // Action is to perform procedure of switching to Master
    },
    {
        StatsAlgoSyncType::StatsAlgoSyncTypeAF,     // Represent dependencis from AF node
        StatsAlgoRole::StatsAlgoRoleSlave,          // New role is Slave, its previous role was Master
        {
            {},                                     // Cross Pipeline Dependency 0
            {},                                     // Cross Pipeline Dependency 1
            {},                                     // Cross Pipeline Dependency 2
            {},                                     // Cross Pipeline Dependency 3
            {}                                      // Cross Pipeline Dependency 4
        },
        StatsAlgoProcessSwitchingSlave              // Action is to perform procedure of switching to Slave
    }
};

// Dependency table for Singleton Algo usecase
MultiStatsDependency singletonDependencyTable[] =
{
    {
        StatsAlgoSyncType::StatsAlgoSyncTypeAECAWB, // Represent dependencis from Stats node (Only AEC/AWB)
        StatsAlgoRole::StatsAlgoRoleMaster,         // Role is Master
        {
            {PropertyIDCrossAECStats, TRUE},        // Cross Pipeline Dependency 0
            {PropertyIDCrossAWBStats, TRUE},        // Cross Pipeline Dependency 1
            {},                                     // Cross Pipeline Dependency 2
            {},                                     // Cross Pipeline Dependency 3
            {}                                      // Cross Pipeline Dependency 4
        },
        StatsAlgoProcessRequest                     // Action is to perform process request
    },
    {
        StatsAlgoSyncType::StatsAlgoSyncTypeAECAWB, // Represent dependencis from Stats node (Only AEC/AWB)
        StatsAlgoRole::StatsAlgoRoleSlave,          // Role is Slave
        {
            {PropertyIDCrossAECStats, TRUE},        // Cross Pipeline Dependency 0
            {PropertyIDCrossAWBStats, TRUE},        // Cross Pipeline Dependency 1
            {},                                     // Cross Pipeline Dependency 2
            {},                                     // Cross Pipeline Dependency 3
            {}                                      // Cross Pipeline Dependency 4
        },
        StatsAlgoProcessRequest                     // Action is to perform process request
    },
    {
        StatsAlgoSyncType::StatsAlgoSyncTypeAF,     // Represent dependencis from AF node
        StatsAlgoRole::StatsAlgoRoleMaster,         // Role is Master
        {
            {PropertyIDCrossAFStats, TRUE},         // Cross Pipeline Dependency 0
            {},                                     // Cross Pipeline Dependency 1
            {},                                     // Cross Pipeline Dependency 2
            {},                                     // Cross Pipeline Dependency 3
            {}                                      // Cross Pipeline Dependency 4
        },
        StatsAlgoProcessRequest                     // Action is to perform process request
    },
    {
        StatsAlgoSyncType::StatsAlgoSyncTypeAF,     // Represent dependencis from AF node
        StatsAlgoRole::StatsAlgoRoleSlave,          // Role is Slave
        {
            {PropertyIDCrossAFStats, TRUE},         // Cross Pipeline Dependency 0
            {},                                     // Cross Pipeline Dependency 1
            {},                                     // Cross Pipeline Dependency 2
            {},                                     // Cross Pipeline Dependency 3
            {}                                      // Cross Pipeline Dependency 4
        },
        StatsAlgoProcessRequest                     // Action is to perform process request
    }
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MultiStatsOperator::SetStatsAlgoRole
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID MultiStatsOperator::SetStatsAlgoRole(
    StatsAlgoRole algoRole)
{
    m_multiStatsData.algoRole = algoRole;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MultiStatsOperator::GetStatsAlgoRole
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
StatsAlgoRole MultiStatsOperator::GetStatsAlgoRole()
{
    return m_multiStatsData.algoRole;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MultiStatsOperator::PrintDependency
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID MultiStatsOperator::PrintDependency(
    DependencyUnit*     pStatsDependencies)
{
    UINT index = 0;

    if (NULL == pStatsDependencies)
    {
        CAMX_LOG_ERROR(CamxLogGroupStats, "Null pointer!");
        return;
    }

    CAMX_LOG_INFO(CamxLogGroupStats, "Debug Dependency 1/3: Mask:0x%x Buff:%d Prop:%d",
        pStatsDependencies->dependencyFlags.dependencyFlagsMask,
        pStatsDependencies->dependencyFlags.hasBufferDependency,
        pStatsDependencies->dependencyFlags.hasPropertyDependency);

    CAMX_LOG_INFO(CamxLogGroupStats, "Debug Dependency 2/3: Buffer Dependency Count:%d",
        pStatsDependencies->bufferDependency.fenceCount);
    for (index = 0; index < pStatsDependencies->bufferDependency.fenceCount; index++)
    {
        CAMX_LOG_INFO(CamxLogGroupStats, "Debug Dependency 2/3_%d: Fence:%d Signaled:%s",
            index,
            *(pStatsDependencies->bufferDependency.phFences[index]),
            *(pStatsDependencies->bufferDependency.pIsFenceSignaled[index]) ? "Yes":"No");
    }

    CAMX_LOG_INFO(CamxLogGroupStats, "Debug Dependency 3/3: Property Dependency Count:%d",
        pStatsDependencies->propertyDependency.count);
    for (index = 0; index < pStatsDependencies->propertyDependency.count; index++)
    {
        CAMX_LOG_INFO(CamxLogGroupStats, "Debug Dependency 3/3_%d: Property:%X Pipeline:%d",
            index,
            pStatsDependencies->propertyDependency.properties[index],
            pStatsDependencies->propertyDependency.pipelineIds[index]);
    }

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MultiStatsOperator::AddDependency
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult MultiStatsOperator::AddDependency(
    PropertyPair*           pPropertyPair,
    DependencyUnit*         pDependencyUnit,
    UINT64                  requestId,
    UINT64                  offset,
    BOOL                    negative,
    BOOL*                   pIsSatisfied)
{
    UINT        pipelineId  = 0;
    CamxResult  result      = CamxResultSuccess;

    if (pPropertyPair->isPeerDependency)
    {
        // Dependency is peer pipeline property, will check from peer pipeline
        pipelineId  = m_multiStatsData.peerPipelineId;
    }
    else
    {
        // Dependency is own pipeline property, will check from own pipeline
        pipelineId = m_multiStatsData.pipelineId;
    }

    // Dependent property need add to DRQ, and claiming "has property dependency"
    UINT32 count = pDependencyUnit->propertyDependency.count;

    pDependencyUnit->propertyDependency.properties[count]   = pPropertyPair->propertyIDs;
    pDependencyUnit->propertyDependency.pipelineIds[count]  = pipelineId;
    pDependencyUnit->propertyDependency.offsets[count]      = offset;
    pDependencyUnit->propertyDependency.negate[count]       = negative;
    pDependencyUnit->propertyDependency.count++;

    pDependencyUnit->dependencyFlags.hasPropertyDependency = TRUE;
    *pIsSatisfied = FALSE;
    CAMX_LOG_VERBOSE(CamxLogGroupStats, "Add Prop:%X|Pipeline:%d|Offset:%lld for type:%d role:%d Req:%llu Pipeline:%d",
                     pPropertyPair->propertyIDs,
                     pipelineId,
                     negative ? (-1 * offset) : offset,
                     m_multiStatsData.algoSyncType,
                     m_multiStatsData.algoRole,
                     requestId,
                     m_multiStatsData.pipelineId);

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// QTIMultiStatsOperator::Initialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID QTIMultiStatsOperator::Initialize(
    const MultiStatsData* pMultiStatsData)
{
    CAMX_ASSERT(NULL != pMultiStatsData);

    m_multiStatsData.algoSyncType       = pMultiStatsData->algoSyncType;
    m_multiStatsData.algoRole           = pMultiStatsData->algoRole;
    m_multiStatsData.peerPipelineId     = pMultiStatsData->peerPipelineId;
    m_multiStatsData.pipelineId         = pMultiStatsData->pipelineId;
    m_multiStatsData.pHwContext         = pMultiStatsData->pHwContext;
    m_multiStatsData.algoAction         = StatsAlgoProcessRequest;
    m_multiStatsData.isSlaveOperational = TRUE;
    m_multiStatsData.pNode              = pMultiStatsData->pNode;

    CAMX_ASSERT(StatsAlgoSyncType::StatsAlgoSyncTypeAECAWB  == m_multiStatsData.algoSyncType ||
                StatsAlgoSyncType::StatsAlgoSyncTypeAF      == m_multiStatsData.algoSyncType);
    CAMX_ASSERT(StatsAlgoRole::StatsAlgoRoleDefault == m_multiStatsData.algoRole ||
                StatsAlgoRole::StatsAlgoRoleMaster  == m_multiStatsData.algoRole ||
                StatsAlgoRole::StatsAlgoRoleSlave   == m_multiStatsData.algoRole);
    CAMX_ASSERT(NULL != m_multiStatsData.pHwContext);
    CAMX_ASSERT(NULL != m_multiStatsData.pNode);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// QTIMultiStatsOperator::UpdateStatsDependencies
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult QTIMultiStatsOperator::UpdateStatsDependencies(
    DependencyUnit*         pStatsDependencies,
    UINT64                  requestId,
    MultiRequestSyncData*   pMultiRequestData,
    BOOL*                   pIsSatisfied)
{
    CamxResult            result                = CamxResultSuccess;
    UINT                  index                 = 0;
    MultiStatsDependency* pMultiStatsDependency = NULL;
    BOOL                  isSwitched            = FALSE;
    INT64                 lpmRequestDelta       = 0;

    CAMX_LOG_VERBOSE(CamxLogGroupStats, "Update dependency for Node:%s Master:%d Req:%llu Pipeline:%d",
                     m_multiStatsData.pNode->Name(),
                     (m_multiStatsData.algoRole == StatsAlgoRoleMaster) ? 1 : 0,
                     requestId,
                     m_multiStatsData.pipelineId);

    result = ReadVendorTag(requestId, &isSwitched);

    if (TRUE == isSwitched)
    {
        CAMX_LOG_VERBOSE(CamxLogGroupStats, "Role switched to %s for Req:%llu",
            (m_multiStatsData.algoRole == StatsAlgoRoleMaster) ? "Master" : "Slave",
            requestId);
    }

    if (CamxResultSuccess != result)
    {
        CAMX_LOG_ERROR(CamxLogGroupStats, "Something went wrong when reading vendor tag for Req:%llu", requestId);
        result = CamxResultEFailed;
    }
    else
    {
        BOOL needAddDependency = CheckIfAddDependency(requestId, pMultiRequestData, &lpmRequestDelta);

        if (TRUE == needAddDependency)
        {
            // No role switch, add dependency for master/follower.
            pMultiStatsDependency = NULL;
            for (index = 0; index < sizeof(qtiDependencyTable) / sizeof(MultiStatsDependency); index++)
            {
                if (qtiDependencyTable[index].algoRole == m_multiStatsData.algoRole)
                {
                    if (qtiDependencyTable[index].algoSyncType == m_multiStatsData.algoSyncType)
                    {
                        // Found matched item
                        pMultiStatsDependency = &(qtiDependencyTable[index]);
                        break;
                    }
                }
            }

            if (NULL != pMultiStatsDependency)
            {
                index = 0;
                while ((MaxCrossPipelineProperties > index) && (0 != pMultiStatsDependency->propertyPair[index].propertyIDs))
                {
                    INT64 offset = 0;
                    if (StatsAlgoRoleMaster == pMultiStatsDependency->algoRole)
                    {
                        // For master, it will depend on slave's stats property for previous request ID
                        offset = lpmRequestDelta + 1;
                    }
                    else
                    {
                        // For slave, it will depend on master's stats property for same request ID
                        offset = lpmRequestDelta;
                    }

                    result = AddDependency(&(pMultiStatsDependency->propertyPair[index]),
                                           pStatsDependencies,
                                           requestId,
                                           static_cast<UINT64>(abs(offset)),
                                           (offset < 0),
                                           pIsSatisfied);
                    if (CamxResultSuccess == result)
                    {
                        index++;
                    }
                    else
                    {
                        CAMX_LOG_ERROR(CamxLogGroupStats, "Add property %X to dependency failed",
                                       pMultiStatsDependency->propertyPair[index].propertyIDs);
                        break;
                    }
                }

                if (CamxResultSuccess == result)
                {
                    m_multiStatsData.algoAction = pMultiStatsDependency->algoAction;
                }
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupStats, "No match item found");
                result = CamxResultENoSuch;
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// QTIMultiStatsOperator::GetStatsAlgoAction
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
StatsAlgoAction QTIMultiStatsOperator::GetStatsAlgoAction()
{
    return m_multiStatsData.algoAction;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// QTIMultiStatsOperator::ReadVendorTag
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult QTIMultiStatsOperator::ReadVendorTag(
    UINT64      requestId,
    BOOL*       pIsSwitched)
{
    CamxResult  result              = CamxResultSuccess;
    const CHAR* pTagSection         = "com.qti.chi.multicamerainfo";
    const CHAR* pTagNameMasterSlave = "MasterCamera";
    UINT        tag[1]              = { 0 };
    VOID*       pData[1]            = { 0 };
    UINT        length              = CAMX_ARRAY_SIZE(tag);
    UINT64      tagOffset[1]        = { 0 };

    // Query information sent by multi camera controller
    VendorTagManager::QueryVendorTagLocation(pTagSection, pTagNameMasterSlave, &tag[0]);
    tag[0] |= InputMetadataSectionMask;

    m_multiStatsData.pNode->GetDataList(tag, pData, tagOffset, length);

    // Check Master/Slave information
    if (NULL != pData[0])
    {
        BOOL* pIsMaster = reinterpret_cast<BOOL*>(pData[0]);
        CAMX_LOG_VERBOSE(CamxLogGroupStats, "Multi camera vendor tag value:%d req:%llu pipeline:%d",
                         *pIsMaster, requestId, m_multiStatsData.pipelineId);

        StatsAlgoRole algoRole = *pIsMaster ? StatsAlgoRoleMaster : StatsAlgoRoleSlave;
        if (m_multiStatsData.algoRole == algoRole)
        {
            *pIsSwitched = FALSE;
        }
        else
        {
            m_multiStatsData.algoRole = algoRole;
            *pIsSwitched = TRUE;
        }
    }
    else
    {
        CAMX_LOG_INFO(CamxLogGroupStats, "Multi camera vendor tag not received for request %llu", requestId);
        *pIsSwitched = FALSE;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// QTIMultiStatsOperator::CheckIfAddDependency
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL QTIMultiStatsOperator::CheckIfAddDependency(
    UINT64                  requestId,
    MultiRequestSyncData*   pMultiRequestData,
    INT64*                  pLPMRequestDelta)
{
    BOOL needAddDependency = FALSE;

    // First request for master doesn't need any additional dependencies
    if (FirstValidRequestId == requestId && StatsAlgoRoleMaster == m_multiStatsData.algoRole)
    {
        needAddDependency = FALSE;
    }
    else
    {
        if (FALSE == pMultiRequestData->isMultiRequest) // No Operation on Slave
        {
            if (TRUE == m_multiStatsData.isSlaveOperational &&
                StatsAlgoRoleMaster == m_multiStatsData.algoRole)
            {
                // Although LPM says NoOp, but if previously slave is operational,
                // for master camera, we still let it depend on previous slave.
                needAddDependency = TRUE;
            }
            else
            {
                // Otherwise, for all requests where NoOp was received,
                // We don't need to add any dependencies.
                needAddDependency = FALSE;
            }
        }
        else // Operational on Slave
        {
            if (FALSE == m_multiStatsData.isSlaveOperational &&
                StatsAlgoRoleMaster == m_multiStatsData.algoRole)
            {
                // Although LPM says Op, but if previously slave is NoOp,
                // for master camera, we should NOT let it depend on previous slave.
                needAddDependency = FALSE;
            }
            else
            {
                // Otherwise, for all requests where Op was received,
                // We need to add related dependencies.
                needAddDependency = TRUE;
            }
        }
    }

    m_multiStatsData.isSlaveOperational = pMultiRequestData->isMultiRequest;
    *pLPMRequestDelta = requestId - pMultiRequestData->requestID[m_multiStatsData.peerPipelineId];
    CAMX_LOG_VERBOSE(CamxLogGroupStats, "Check add dependency %s, Req:%llu Pipeline:%d MultiReq:%d Delta:%lld needAdd:%d",
                     m_multiStatsData.pNode->Name(),
                     requestId,
                     m_multiStatsData.pipelineId,
                     pMultiRequestData->isMultiRequest,
                     *pLPMRequestDelta,
                     needAddDependency);

    return needAddDependency;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// QTIMultiStatsOperator::RemoveDependency
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID QTIMultiStatsOperator::RemoveDependency(
    StatsDependency* pStatsDependency)
{
    CAMX_UNREFERENCED_PARAM(pStatsDependency);
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SingletonStatsOperator::Initialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID SingletonStatsOperator::Initialize(
    const MultiStatsData* pMultiStatsData)
{
    CAMX_ASSERT(NULL != pMultiStatsData);

    m_multiStatsData.algoSyncType       = pMultiStatsData->algoSyncType;
    m_multiStatsData.algoRole           = pMultiStatsData->algoRole;
    m_multiStatsData.peerPipelineId     = pMultiStatsData->peerPipelineId;
    m_multiStatsData.pipelineId         = pMultiStatsData->pipelineId;
    m_multiStatsData.pHwContext         = pMultiStatsData->pHwContext;
    m_multiStatsData.algoAction         = StatsAlgoProcessRequest;
    m_multiStatsData.isSlaveOperational = TRUE;
    m_multiStatsData.pNode              = pMultiStatsData->pNode;

    CAMX_ASSERT(StatsAlgoSyncType::StatsAlgoSyncTypeAECAWB  == m_multiStatsData.algoSyncType ||
                StatsAlgoSyncType::StatsAlgoSyncTypeAF      == m_multiStatsData.algoSyncType);
    CAMX_ASSERT(StatsAlgoRole::StatsAlgoRoleDefault == m_multiStatsData.algoRole ||
                StatsAlgoRole::StatsAlgoRoleMaster  == m_multiStatsData.algoRole ||
                StatsAlgoRole::StatsAlgoRoleSlave   == m_multiStatsData.algoRole);
    CAMX_ASSERT(NULL != m_multiStatsData.pHwContext);
    CAMX_ASSERT(NULL != m_multiStatsData.pNode);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SingletonStatsOperator::UpdateStatsDependencies
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult SingletonStatsOperator::UpdateStatsDependencies(
    DependencyUnit*         pStatsDependencies,
    UINT64                  requestId,
    MultiRequestSyncData*   pMultiRequestData,
    BOOL*                   pIsSatisfied)
{
    CamxResult            result                = CamxResultSuccess;
    UINT                  index                 = 0;
    MultiStatsDependency* pMultiStatsDependency = NULL;
    BOOL                  isSwitched            = FALSE;
    INT64                 lpmRequestDelta       = 0;

    CAMX_LOG_VERBOSE(CamxLogGroupStats, "Update dependency for Node:%s Master:%d Req:%llu Pipeline:%d",
                     m_multiStatsData.pNode->Name(),
                     (m_multiStatsData.algoRole == StatsAlgoRoleMaster) ? 1 : 0,
                     requestId,
                     m_multiStatsData.pipelineId);

    result = ReadVendorTag(requestId, &isSwitched);

    if (TRUE == isSwitched)
    {
        CAMX_LOG_VERBOSE(CamxLogGroupStats, "Role switched to %s for Req:%llu",
            (m_multiStatsData.algoRole == StatsAlgoRoleMaster) ? "Master" : "Slave",
            requestId);
    }

    if (CamxResultSuccess != result)
    {
        CAMX_LOG_ERROR(CamxLogGroupStats, "Something went wrong when checking role switch for Req:%llu", requestId);
        result = CamxResultEFailed;
    }
    else
    {
        BOOL needAddDependency = CheckIfAddDependency(requestId, pMultiRequestData, &lpmRequestDelta);

        pMultiStatsDependency = NULL;
        for (index = 0; index < sizeof(singletonDependencyTable) / sizeof(MultiStatsDependency); index++)
        {
            if (singletonDependencyTable[index].algoRole == m_multiStatsData.algoRole)
            {
                if (singletonDependencyTable[index].algoSyncType == m_multiStatsData.algoSyncType)
                {
                    // Found matched item
                    pMultiStatsDependency = &(singletonDependencyTable[index]);
                    break;
                }
            }
        }

        if (NULL != pMultiStatsDependency)
        {
            if (TRUE == needAddDependency)
            {
                index = 0;
                while ((MaxCrossPipelineProperties > index) && (0 != pMultiStatsDependency->propertyPair[index].propertyIDs))
                {
                    INT64 offset = 0;
                    if (StatsAlgoRoleMaster == pMultiStatsDependency->algoRole)
                    {
                        // For master, it will depend on slave's stats property for previous request ID
                        offset = lpmRequestDelta + 1;
                    }
                    else if (StatsAlgoRoleSlave == pMultiStatsDependency->algoRole)
                    {
                        // For slave, it will depend on master's stats property for same request ID
                        offset = lpmRequestDelta;
                    }
                    else
                    {
                        CAMX_LOG_ERROR(CamxLogGroupStats, "Invalid Statistic algorithm role type");
                        result = CamxResultENoSuch;
                    }

                    if (CamxResultSuccess == result)
                    {
                        result = AddDependency(&(pMultiStatsDependency->propertyPair[index]),
                                               pStatsDependencies,
                                               requestId,
                                               static_cast<UINT64>(abs(offset)),
                                               (offset <= 0),
                                               pIsSatisfied);
                        if (CamxResultSuccess == result)
                        {
                            index++;
                            if (FALSE == *pIsSatisfied)
                            {
                                pStatsDependencies->dependencyFlags.hasPropertyDependency = TRUE;
                            }
                        }
                        else
                        {
                            CAMX_LOG_ERROR(CamxLogGroupStats, "Failed to add property %X to dependency",
                                           pMultiStatsDependency->propertyPair[index].propertyIDs);
                            break;
                        }
                    }
                }
            }

            if (CamxResultSuccess == result)
            {
                m_multiStatsData.algoAction = pMultiStatsDependency->algoAction;
            }
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupStats, "No match item found");
            result = CamxResultENoSuch;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SingletonStatsOperator::GetStatsAlgoAction
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
StatsAlgoAction SingletonStatsOperator::GetStatsAlgoAction()
{
    return m_multiStatsData.algoAction;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SingletonStatsOperator::ReadVendorTag
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult SingletonStatsOperator::ReadVendorTag(
    UINT64      requestId,
    BOOL*       pIsSwitched)
{
    CamxResult  result              = CamxResultSuccess;
    const CHAR* pTagSection         = "com.qti.chi.multicamerainfo";
    const CHAR* pTagNameMasterSlave = "MasterCamera";
    UINT        tag[1]              = { 0 };
    VOID*       pData[1]            = { 0 };
    UINT        length              = CAMX_ARRAY_SIZE(tag);
    UINT64      tagOffset[1]        = { 0 };

    // Query information sent by multi camera controller
    VendorTagManager::QueryVendorTagLocation(pTagSection, pTagNameMasterSlave, &tag[0]);
    tag[0] |= InputMetadataSectionMask;

    m_multiStatsData.pNode->GetDataList(tag, pData, tagOffset, length);

    // Check Master/Slave information
    if (NULL != pData[0])
    {
        BOOL* pIsMaster = reinterpret_cast<BOOL*>(pData[0]);
        CAMX_LOG_VERBOSE(CamxLogGroupStats, "Multi camera vendor tag value:%d req:%llu pipeline:%d",
                         *pIsMaster, requestId, m_multiStatsData.pipelineId);

        StatsAlgoRole algoRole = *pIsMaster ? StatsAlgoRoleMaster : StatsAlgoRoleSlave;
        if (m_multiStatsData.algoRole == algoRole)
        {
            *pIsSwitched = FALSE;
        }
        else
        {
            m_multiStatsData.algoRole = algoRole;
            *pIsSwitched = TRUE;
        }
    }
    else
    {
        CAMX_LOG_INFO(CamxLogGroupStats, "Multi camera vendor tag not received for request %llu", requestId);
        *pIsSwitched = FALSE;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SingletonStatsOperator::CheckIfAddDependency
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL SingletonStatsOperator::CheckIfAddDependency(
    UINT64                  requestId,
    MultiRequestSyncData*   pMultiRequestData,
    INT64*                  pLPMRequestDelta)
{
    BOOL needAddDependency = FALSE;

    // First request for master doesn't need any additional dependencies
    if (FirstValidRequestId == requestId && StatsAlgoRoleMaster == m_multiStatsData.algoRole)
    {
        needAddDependency = FALSE;
    }
    else
    {
        if (FALSE == pMultiRequestData->isMultiRequest) // No Operation on Slave
        {
            if (TRUE == m_multiStatsData.isSlaveOperational &&
                StatsAlgoRoleMaster == m_multiStatsData.algoRole)
            {
                // Although LPM says NoOp, but if previously slave is operational,
                // for master camera, we still let it depend on previous slave.
                needAddDependency = TRUE;
            }
            else
            {
                // Otherwise, for all requests where NoOp was received,
                // We don't need to add any dependencies.
                needAddDependency = FALSE;
            }
        }
        else // Operational on Slave
        {
            if (FALSE == m_multiStatsData.isSlaveOperational &&
                StatsAlgoRoleMaster == m_multiStatsData.algoRole)
            {
                // Although LPM says Op, but if previously slave is NoOp,
                // for master camera, we should NOT let it depend on previous slave.
                needAddDependency = FALSE;
            }
            else
            {
                // Otherwise, for all requests where Op was received,
                // We need to add related dependencies.
                needAddDependency = TRUE;
            }
        }
    }

    m_multiStatsData.isSlaveOperational = pMultiRequestData->isMultiRequest;
    *pLPMRequestDelta = requestId - pMultiRequestData->requestID[m_multiStatsData.peerPipelineId];
    CAMX_LOG_VERBOSE(CamxLogGroupStats, "Check add dependency %s, Req:%llu Pipeline:%d MultiReq:%d Delta:%lld needAdd:%d",
                     m_multiStatsData.pNode->Name(),
                     requestId,
                     m_multiStatsData.pipelineId,
                     pMultiRequestData->isMultiRequest,
                     *pLPMRequestDelta,
                     needAddDependency);

    return needAddDependency;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SingletonStatsOperator::RemoveDependency
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID SingletonStatsOperator::RemoveDependency(
    StatsDependency* pStatsDependency)
{
    CAMX_UNREFERENCED_PARAM(pStatsDependency);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NoSyncStatsOperator::Initialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID NoSyncStatsOperator::Initialize(
    const MultiStatsData* pMultiStatsData)
{
    CAMX_UNREFERENCED_PARAM(pMultiStatsData);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NoSyncStatsOperator::UpdateStatsDependencies
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NoSyncStatsOperator::UpdateStatsDependencies(
    DependencyUnit*         pStatsDependencies,
    UINT64                  requestId,
    MultiRequestSyncData*   pMultiRequestData,
    BOOL*                   pIsSatisfied)
{
    CAMX_UNREFERENCED_PARAM(pStatsDependencies);
    CAMX_UNREFERENCED_PARAM(requestId);
    CAMX_UNREFERENCED_PARAM(pMultiRequestData);
    CAMX_UNREFERENCED_PARAM(pIsSatisfied);

    *pIsSatisfied = TRUE;

    return CamxResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NoSyncStatsOperator::GetStatsAlgoAction
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
StatsAlgoAction NoSyncStatsOperator::GetStatsAlgoAction()
{
    return StatsAlgoProcessRequest;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NoSyncStatsOperator::ReadVendorTag
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult NoSyncStatsOperator::ReadVendorTag(
    UINT64      requestId,
    BOOL*       pIsSwitched)
{
    CAMX_UNREFERENCED_PARAM(requestId);
    CAMX_UNREFERENCED_PARAM(pIsSwitched);

    return CamxResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NoSyncStatsOperator::CheckIfAddDependency
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL NoSyncStatsOperator::CheckIfAddDependency(
    UINT64                  requestId,
    MultiRequestSyncData*   pMultiRequestData,
    INT64*                  pLPMRequestDelta)
{
    CAMX_UNREFERENCED_PARAM(requestId);
    CAMX_UNREFERENCED_PARAM(pMultiRequestData);
    CAMX_UNREFERENCED_PARAM(pLPMRequestDelta);

    // NoSyncStatsOperator doesn't need add dependency
    return FALSE;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NoSyncStatsOperator::RemoveDependency
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID NoSyncStatsOperator::RemoveDependency(
    StatsDependency* pStatsDependency)
{
    CAMX_UNREFERENCED_PARAM(pStatsDependency);
}

CAMX_NAMESPACE_END
